package com.riqthen.superedittext

import android.content.ClipboardManager
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.os.Build
import android.support.annotation.StringRes
import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat
import android.support.v7.content.res.AppCompatResources
import android.support.v7.widget.AppCompatEditText
import android.text.*
import android.text.method.DigitsKeyListener
import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.util.AttributeSet
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View.OnFocusChangeListener

/**
 * @author H
 * @createDate 2018/9/13 18:30
 * @description 系统默认textSize是18sp
 * @see https://github.com/woxingxiao/XEditText
 * @since
 */
class SuperEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) : AppCompatEditText(context, attrs, defStyleAttr) {

    private var mClearEnable: Boolean = false
    private var mClearResId: Int = 0
    private var mTogglePasswordEnable: Boolean = false
    private var mEmojiEnable: Boolean = false
    private var mPasswordShowResId: Int = 0
    private var mPasswordHideResId: Int = 0
    private var mTextSizeDefault: Float = 0f
    private var mTextSizeInput: Float = 0f

    private var mClearDrawable: Drawable? = null
    private var mTogglePwdDrawable: Drawable? = null
    private val mTextWatcher: TextWatcher
    private var mHasFocused: Boolean = false
    private var mIsPasswordType: Boolean = false
    private var mIsPasswordShow: Boolean = false
    private var mBitmap: Bitmap? = null
    private var mLeft: Int = 0
    private var mTop: Int = 0

    private val isTextEmpty: Boolean
        get() = text!!.toString().trim { it <= ' ' }.isEmpty()

    init {
        initAttrs(context, attrs, defStyleAttr)
        if (!mEmojiEnable) {
            filters = arrayOf<InputFilter>(EmojiExcludeFilter())
        }
        mTextWatcher = MyTextWatcher()
        addTextChangedListener(mTextWatcher)

        onFocusChangeListener = OnFocusChangeListener { _, hasFocus ->
            mHasFocused = hasFocus
            logicOfCompoundDrawables()
        }
    }

    private fun initAttrs(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SuperEditText, defStyleAttr, 0)
        mClearEnable = typedArray.getBoolean(R.styleable.SuperEditText_super_enableClear, false)
        mTogglePasswordEnable = typedArray.getBoolean(R.styleable.SuperEditText_super_enablePasswordToggle, false)
        mEmojiEnable = typedArray.getBoolean(R.styleable.SuperEditText_super_enableEmoji, true)
        mClearResId = typedArray.getResourceId(R.styleable.SuperEditText_super_iconClear, -1)
        mPasswordShowResId = typedArray.getResourceId(R.styleable.SuperEditText_super_iconPasswordShow, -1)
        mPasswordHideResId = typedArray.getResourceId(R.styleable.SuperEditText_super_iconPasswordHide, -1)
        mTextSizeDefault = typedArray.getDimension(R.styleable.SuperEditText_super_textSizeDefault, 0f)   //系统默认的EditText的textSize为18sp
        mTextSizeInput = typedArray.getDimension(R.styleable.SuperEditText_super_textSizeInput, 0f)
        typedArray.recycle()
        if (mClearEnable) {
            if (mClearResId == -1) {
                mClearResId = R.drawable.svg_ic_clear_24dp
            }
            mClearDrawable = AppCompatResources.getDrawable(context, mClearResId)
            if (mClearDrawable != null) {
                mClearDrawable!!.setBounds(0, 0, mClearDrawable!!.intrinsicWidth, mClearDrawable!!.intrinsicHeight)
                if (mClearResId == R.drawable.svg_ic_clear_24dp) {
                    DrawableCompat.setTint(mClearDrawable!!, currentHintTextColor)
                }
            }
        }
        if (mTogglePasswordEnable) {
            inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
        }
        if (text.toString().isNotEmpty()) {
            if (mTextSizeDefault != 0f) {
                setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeDefault)
            }
        } else {
            if (mTextSizeInput != 0f) {
                setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeInput)
            }
        }
        dealWithInputTypes(true)
    }

    /**
     * set true to disable Emoji and special symbol
     * @param emojiEnable true: disable emoji; false: enable emoji
     */
    fun setEmojiEnable(emojiEnable: Boolean) {
        mEmojiEnable = emojiEnable
        filters = if (!emojiEnable) {
            arrayOf<InputFilter>(EmojiExcludeFilter())
        } else {
            arrayOfNulls(0)
        }
    }

    fun setTogglePasswordEnable(togglePasswordEnable: Boolean) {
        mTogglePasswordEnable = togglePasswordEnable
        inputType = if (togglePasswordEnable) {
            InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
        } else {
            InputType.TYPE_CLASS_TEXT
        }
    }

    fun setClearEnable(clearEnable: Boolean) {
        mClearEnable = clearEnable
        if (mClearEnable) {
            if (mClearResId == -1) {
                mClearResId = R.drawable.svg_ic_clear_24dp
            }
            mClearDrawable = AppCompatResources.getDrawable(context, mClearResId)
            if (mClearDrawable != null) {
                mClearDrawable!!.setBounds(0, 0, mClearDrawable!!.intrinsicWidth, mClearDrawable!!.intrinsicHeight)
                if (mClearResId == R.drawable.svg_ic_clear_24dp) {
                    DrawableCompat.setTint(mClearDrawable!!, currentHintTextColor)
                }
            }
        }
    }

    /**
     * @param type
     * InputType.TYPE_CLASS_NUMBER 数字
     * InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL 小数
     */
    override fun setInputType(type: Int) {
        super.setInputType(type)
        dealWithInputTypes(false)
    }

    private fun dealWithInputTypes(fromXml: Boolean) {
        var inputType = inputType
        if (!fromXml) {
            inputType++
            if (inputType == 17) {
                inputType++
            }
        }
        mIsPasswordType = mTogglePasswordEnable && (inputType == 129 || inputType == 18 || inputType == 145 || inputType == 225)
        if (mIsPasswordType) {
            mIsPasswordShow = inputType == 145 // textVisiblePassword
            transformationMethod = if (mIsPasswordShow) {
                HideReturnsTransformationMethod.getInstance()
            } else {
                PasswordTransformationMethod.getInstance()
            }
            if (mPasswordShowResId == -1) {
                mPasswordShowResId = R.drawable.svg_ic_show_password_24dp
            }
            if (mPasswordHideResId == -1) {
                mPasswordHideResId = R.drawable.svg_ic_hide_password_24dp
            }
            val tId = if (mIsPasswordShow) mPasswordShowResId else mPasswordHideResId
            mTogglePwdDrawable = ContextCompat.getDrawable(context, tId)
            if (mPasswordShowResId == R.drawable.svg_ic_show_password_24dp || mPasswordHideResId == R.drawable.svg_ic_hide_password_24dp) {
                DrawableCompat.setTint(mTogglePwdDrawable!!, currentHintTextColor)
            }
            mTogglePwdDrawable!!.setBounds(0, 0, mTogglePwdDrawable!!.intrinsicWidth, mTogglePwdDrawable!!.intrinsicHeight)
            if (mClearResId == -1) {
                mClearResId = R.drawable.svg_ic_clear_24dp
            }
            if (mClearEnable) {
                mBitmap = getBitmapFromVectorDrawable(context, mClearResId, mClearResId == R.drawable.svg_ic_clear_24dp) // clearDrawable
            }
        }
        if (!fromXml) {
            logicOfCompoundDrawables()
        }
    }

    private fun getBitmapFromVectorDrawable(context: Context, drawableId: Int, tint: Boolean): Bitmap? {
        var drawable: Drawable? = AppCompatResources.getDrawable(context, drawableId) ?: return null

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            drawable = DrawableCompat.wrap(drawable!!).mutate()
        }
        if (tint) {
            DrawableCompat.setTint(drawable!!, currentHintTextColor)
        }
        val bitmap = Bitmap.createBitmap(drawable!!.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
        return bitmap
    }

    private fun logicOfCompoundDrawables() {
        if (!mHasFocused || isTextEmpty && !mIsPasswordType) {
            setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], null, compoundDrawables[3])
            if (!isTextEmpty && mIsPasswordType) {
                invalidate()
            }
        } else {
            if (mIsPasswordType) {
                if (mPasswordShowResId == R.drawable.svg_ic_show_password_24dp || mPasswordHideResId == R.drawable.svg_ic_hide_password_24dp) {
                    DrawableCompat.setTint(mTogglePwdDrawable!!, currentHintTextColor)
                }
                setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], mTogglePwdDrawable, compoundDrawables[3])
            } else if (!isTextEmpty && mClearEnable) {
                setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], mClearDrawable, compoundDrawables[3])
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (mHasFocused && mBitmap != null && mIsPasswordType && !isTextEmpty) {
            if (mLeft * mTop == 0) {
                mLeft = measuredWidth - paddingRight - mTogglePwdDrawable!!.intrinsicWidth - mBitmap!!.width - dp2px(4)
                mTop = measuredHeight - mBitmap!!.height shr 1
            }
            canvas.drawBitmap(mBitmap!!, mLeft.toFloat(), mTop.toFloat(), null)
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
            performClick()
        }
        if (mHasFocused && mIsPasswordType && event.action == MotionEvent.ACTION_UP) {
            val w = mTogglePwdDrawable!!.intrinsicWidth
            val h = mTogglePwdDrawable!!.intrinsicHeight
            val top = measuredHeight - h shr 1
            var right = measuredWidth - paddingRight
            var isAreaX = event.x <= right && event.x >= right - w
            val isAreaY = event.y >= top && event.y <= top + h
            if (isAreaX && isAreaY) {
                mIsPasswordShow = !mIsPasswordShow
                transformationMethod = if (mIsPasswordShow) {
                    HideReturnsTransformationMethod.getInstance()
                } else {
                    PasswordTransformationMethod.getInstance()
                }
                setSelection(selectionStart, selectionEnd)
                mTogglePwdDrawable = ContextCompat.getDrawable(context, if (mIsPasswordShow) mPasswordShowResId else mPasswordHideResId)
                if (mPasswordShowResId == R.drawable.svg_ic_show_password_24dp || mPasswordHideResId == R.drawable.svg_ic_hide_password_24dp) {
                    DrawableCompat.setTint(mTogglePwdDrawable!!, currentHintTextColor)
                }
                mTogglePwdDrawable!!.setBounds(0, 0, mTogglePwdDrawable!!.intrinsicWidth, mTogglePwdDrawable!!.intrinsicHeight)
                setCompoundDrawables(compoundDrawables[0], compoundDrawables[1], mTogglePwdDrawable, compoundDrawables[3])
                invalidate()
            }
            if (mClearEnable) {
                right -= w + dp2px(4)
                isAreaX = event.x <= right && event.x >= right - mBitmap!!.width
                if (isAreaX && isAreaY) {
                    error = null
                    setText("")
                }
            }
        }
        if (mHasFocused && mClearEnable && !mIsPasswordType && event.action == MotionEvent.ACTION_UP) {
            val rect = mClearDrawable!!.bounds
            val rectW = rect.width()
            val rectH = rect.height()
            val top = measuredHeight - rectH shr 1
            val right = measuredWidth - paddingRight
            val isAreaX = event.x <= right && event.x >= right - rectW
            val isAreaY = event.y >= top && event.y <= top + rectH
            if (isAreaX && isAreaY) {
                error = null
                setText("")
            }
        }
        return super.onTouchEvent(event)
    }

    //禁止复制粘贴密码框
    override fun onTextContextMenuItem(id: Int): Boolean {
        val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        if (id == 16908320 || id == 16908321) { // catch CUT or COPY ops
            super.onTextContextMenuItem(id)
            val clip = clipboardManager.primaryClip
            val item = clip?.getItemAt(0)
            if (item != null && item.text != null) {
                return true
            }
        } else if (id == 16908322) { // catch PASTE ops
            val clip = clipboardManager.primaryClip
            val item = clip?.getItemAt(0)
            if (item != null && item.text != null) {
                return true
            }
        }
        return super.onTextContextMenuItem(id)
    }

    private fun dp2px(dp: Int): Int {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics).toInt()
    }

    private fun sp2px(context: Context, spValue: Float): Int {
        val fontScale = context.resources.displayMetrics.scaledDensity
        return (spValue * fontScale + 0.5f).toInt()
    }

    fun setTextSizeDefault(textSizeDefault: Float) {
        mTextSizeDefault = sp2px(context, textSizeDefault).toFloat()
        textSize = textSizeDefault
    }

    fun setTextSizeInput(textSizeInput: Float) {
        mTextSizeInput = sp2px(context, textSizeInput).toFloat()
    }

    fun setDigits(digits: String) {
        keyListener = DigitsKeyListener.getInstance(digits)
    }

    fun setDigits(@StringRes digitsRes: Int) {
        keyListener = DigitsKeyListener.getInstance(context.resources.getString(digitsRes))
    }

    // =========================== MyTextWatcher ================================
    private inner class MyTextWatcher : TextWatcher {

        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            if (TextUtils.isEmpty(s)) {
                if (mTextSizeDefault != 0f) {
                    setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeDefault)
                }
            } else {
                if (mTextSizeInput != 0f) {
                    setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeInput)
                }
            }
        }

        override fun afterTextChanged(s: Editable) {
            logicOfCompoundDrawables()
            removeTextChangedListener(mTextWatcher)
            addTextChangedListener(mTextWatcher)
        }
    }

    //过滤emoji
    private inner class EmojiExcludeFilter : InputFilter {

        override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
            for (i in start until end) {
                val type = Character.getType(source[i])
                if (type == Character.SURROGATE.toInt() || type == Character.OTHER_SYMBOL.toInt()) {
                    return ""
                }
            }
            return null
        }
    }
}